4. Main Analysis
The roadmap for the main analysis follows from the 4 questions we pose in the introduction part.
4.1. What are the review activity trackings for different electronic products?
Since each product is launched at a different time, their user reviews are also active during different periods across the decade (2008 - 2018). We use strip chart to visualize when these reviews are clustered for each product. Also, it tells us approximately when a product launched –> gained popularity –> stabilize –> decline among population’s discussion.
yx_select <- select(electron_data, name, reviews.date, reviews.rating)
yx_select <- yx_select[rowSums(is.na(yx_select)) == 0, ] #remove na rows
yx_select$reviews.date <- as.Date(yx_select$reviews.date)
ggplot(data=yx_select, aes(x=name, y=reviews.date, color=name)) +
geom_point(size=1.5) + theme(legend.position="none", plot.title = element_text(size=20, face="bold"),
text = element_text(size=6), axis.title = element_text(size=15, face="bold")) +
ggtitle("Review activity trackings") +
coord_flip()
THe first place to get insight is the time tracking of review activity. The plot shows the review date range of all products so that we can have a clear viewpoint about when the first and last reviews were produced. It indicates most of the reviews have a date range of 4 years from 2014 to 2018.
Next, we specifically discuss two metrics that imply popularity of products – number of reviews commented and average rating given. Furthermore, we try to compare the trend between these two metrics. Does more reviews and high ratings imply one another?
From the plot, for example, the review activity of Acoustimass Speaker is bimodal. It reached its heyday from 2013 to 2015. After 2015, the reviews activity declined heavily, but from 2017, it recovered its popularity.
review_count_select <- yx_select %>%
group_by(name, month = floor_date(reviews.date, unit = "month")) %>%
summarise(n = n()) %>% mutate(freq = n/sum(n))
selected1 = review_count_select[
review_count_select$name == c("Alpine" ),]
average_rating_select <- yx_select %>%
group_by(name, month = floor_date(reviews.date, unit = "month")) %>%
summarise(average.rating = mean(reviews.rating, na.rm=TRUE))
selected2 = average_rating_select[
average_rating_select$name == c("Alpine"),]
selected <- data.frame(selected1, selected2$average.rating)
colnames(selected)[5] = 'average.rating'
p <- ggplot(selected, aes(x = month))
p <- p + geom_line(aes(y = freq, colour = "Frequency"))
p <- p + geom_line(aes(y = average.rating/30, colour = "Ave_rating"))
p <- p + scale_y_continuous(sec.axis = sec_axis(~.*30, name = "Average Rate stars"))
p <- p + scale_colour_manual(values = c("steelblue2", "orange1"))
p <- p + labs(y = "Review Frequency",
x = "Month",
colour = "Parameter")
p <- p + theme(legend.position = c(0.8, 0.5)) +ggtitle('Comparsion of Average rate and Review Frequency Over time') + theme_gray(base_size = 14)
p
In the plot above, we take product “Alpine” as an example. It implies the average rating of users has the similar trend with review number. Besides, we can see the trends from the time series plot that there is a burst of review number around March 2016 and a sharp decrease of average rating around September 2016. Because all 50 products will make the plot too messy, we use shiny app (in Interactive Component) which allows user to switch different products to see their trends.
For Alpine, there is no obvious common pattern between average rating and review frequency, which implies a weak relationship between these two variables.
4.2. What is the correlation between different dimensions of the review?
In this section, we evalute the review from 4 dimensions:
- popularity – identified by the number of reviews
- reputation – identified by the number of recommendagtions, and the ratings
- sentiment – encoded in the review text
- recommendation – whether or not recommend
Speciel notes on the sentiment score: we use an NLP package sentimentr to convert each review text to a numerical score that approximates the sentiment (polarity) of it. The negative sentiment implies negative sentiment, 0 sentiment score implies neutral sentiment, amnd the positive sentiment score implies positive sentiment.
Since the analysis involves multi-dimensional metrics over different products, we break the problem down: first, we examine each single dimension over different products; then we investigate pairwise, as well as triplet relationship among three metrics.
4.2.1. Dimension-wise analysis
1) Popularity
The popularity, i.e., the numebr of reviews, has been investigated in last question.
2) Recommendation
amazon_electronics_missing_dorecommend_removed <-electron_data[!is.na(electron_data$reviews.doRecommend),]
cbPalette0 <- c("orange1", "steelblue1")
p.recommend <- ggplot(amazon_electronics_missing_dorecommend_removed, aes(x = fct_infreq(name), y = reviews.doRecommend, fill= factor(reviews.doRecommend))) +
geom_bar(stat="identity") +
theme(plot.title = element_text(size = 20, face = "bold"), axis.title=element_text(size=15,face="bold"),
legend.text=element_text(size=15), legend.title=element_text(size=15),legend.position="top",
axis.text.x = element_blank()) +
ggtitle("Recomendations for each product") +
xlab("Product name") + ylab("Count") +
coord_flip() + scale_fill_manual(values=cbPalette0, name="Recommend?")
p.recommend
Observation:
It seems most of users who wrote reviews tend to give “doRecommend”. This applies to the top 5 popular products. Then it becomes important to summarise users comments – their descriptions, key words, etc. Also, as mentioned above, we need to analyze review text of users who do not fill in “do/do not Recommend”.
3) Sentiment score
sen_text <- get_sentences(as.character(electron_data$reviews.text))
sen_text <- sentiment_by(sen_text)
amazon_electronics_with_sentiment <- electron_data
amazon_electronics_with_sentiment['sentiment.score'] <- sen_text$ave_sentiment
ggplot(data=amazon_electronics_with_sentiment, aes(x=name, y=sentiment.score, color=name)) +
geom_point(size=1.5) + theme(legend.position="none", plot.title = element_text(size=20, face="bold"),
text = element_text(size=8), axis.title = element_text(size=15, face="bold")) +
ggtitle("Sentiment score scatterplot by product") +
coord_flip()
Observation
The range / average point / median of sentiment scores vary from product to product. However, from this static plot we could not get more insights: e.g. how do sentiment score correlates with other dimension of review? or what specific text contributes to a low sentiment score? We will answer those questions in the interaction component part.
3) Rating distribution
ratings = electron_data %>%
group_by(name, reviews.rating) %>%
summarise(n = n()) %>%
transmute(reviews.rating, freq = n / sum(n))
ratings$reviews.rating = factor(ratings$reviews.rating)
ratings$freq.good = 2
for( i in 1:nrow(ratings)){
bname = ratings[i,]$name
l5 = ratings %>% filter(name==bname, reviews.rating==5)
l4 = ratings %>% filter(name==bname, reviews.rating==4)
ratings[i,]$freq.good = l5$freq + l4$freq
}
cbPalette <- c("peachpuff1", "orange1", "chocolate3", "steelblue3", "slategray2")
p.rating.dist = ggplot(ratings, aes(x = reorder(name, freq.good), fill=reviews.rating)) +
geom_bar(data = subset(ratings, reviews.rating %in% c(1,2, 3)),
aes(y = -freq), position="stack", stat="identity") +
geom_bar(data = subset(ratings,
reviews.rating %in% c(4,5)),
aes(y = freq),
position = position_stack(reverse = TRUE), stat="identity") +
xlab('product name') + ylab('percentage') +
ggtitle('How customers rate the products?') +
theme(plot.title = element_text(size = 20, face = "bold"), text = element_text(size=10)) +
coord_flip() + scale_fill_manual(values=cbPalette, name="Stars")
p.rating.dist
Observation:
The diverging bar chart suggests that: - As the percentage of 5-star ratings decreases (which is due to the plot rule we define), the ‘center’ of the bar shifts to the left, which suggests the overall rating of the brands are shifting to negative.
- Customers rarely give an extreamely negative rating, which is 1-star. For those brands that have really low percentage of 5-star ratings, the percentage of 1-star ratings are not relatively high compare to other brands. However, the percentage of 3-star ratings increase as that of 5-star rating decrease, which can be an interesting take-away.
4.2.2. Pairwise correlation among dimensions
summarise_table_name <- amazon_electronics_with_sentiment %>%
select(name, reviews.doRecommend, reviews.rating, sentiment.score)%>%
na.omit() %>%
group_by(name) %>%
summarise(n = n(), average.rating = sum(reviews.rating)/n(), average.sentiment = sum(sentiment.score)/n(), prop.recommend = sum(reviews.doRecommend)/n())
ggpairs(summarise_table_name, columns = 2:5, lower = list(combo = wrap("facethist", binwidth = 0.5)))
Observation:
Using pair correlation plot, we can more closely examine the relationship between these metrics. For metrics, number of reviews and review sentiment follow right-skewed distribution; average ratings and proportion or recommend follow left-skewed distribution. For pair relationship, average rating and proportion of recommend are highly positively correlated; most reviews are clustered and associated with high average rating but few of them are associated with high review sentiment (>0.5); also there is no clear positive correlation between high sentiment and high rating.
4.3. For each product, how do ratings and user sentiment change over time?
Approach: in this section, we use both shiny app and d3 visualization to investigate the relationship between ratings and user sentiment over time for each product. Shiny app allows us to view target relationship in each individual product. For the report purpose, we pick two products – “iHome Rechargeable Splash Proof Stereo Bluetooth Speaker”– and Microsoft Surface Pro 4 plot the development of their ratings and sentiment as shown in our shiny app. D3 visualization, besides including per product ratings and sentiment, provides other information such as number of reviews, most positive/negative review, etc.
electron_data$reviews.date <- as.Date(electron_data$reviews.date)
sentiment_df = data.frame(electron_data$name, electron_data$reviews.date,sen_text$ave_sentiment, electron_data$reviews.rating)
colnames(sentiment_df)[1]<-"name"
colnames(sentiment_df)[2]<-"review_date"
colnames(sentiment_df)[3]<-"text_scores"
colnames(sentiment_df)[4]<-"rating"
sentiment_df <- sentiment_df[rowSums(is.na(sentiment_df)) == 0, ] #remove na rows
product <- sentiment_df[sentiment_df$name == unique(sentiment_df$name)[7],]
tidy_table <- product %>% group_by(month = floor_date(review_date, unit = "month")) %>% summarise(ave_review_text_scores = sum(text_scores)/n(), ave_rating = sum(rating)/n())
tidy_table = data.frame(tidy_table)
p1 <- ggplot(tidy_table, aes(x = month))
p1 <- p1 + geom_line(aes(y = ave_review_text_scores, colour = "Sentiment Score"), size = 1)
# adding the relative ave_rating data, transformed to match roughly the range of the sentimental scores
p1 <- p1 + geom_line(aes(y = ave_rating/13, colour = "Ave_rating"), size = 1)
# now adding the secondary axis, following the example in the help file ?scale_y_continuous
# and, very important, reverting the above transformation
p1 <- p1 + scale_y_continuous(sec.axis = sec_axis(~.*13, name = "Average Rate stars"))
# modifying colours and theme options
p1 <- p1 + scale_colour_manual(values = c("steelblue2", "orange1"))
p1 <- p1 + labs(y = "Sentimental Scores",
x = "Month",
colour = "Parameter")
p1 <- p1 + theme(legend.position="none")+ggtitle('Stereo Bluetooth Speaker')
p1 <-p1 + theme_gray(base_size = 14) + theme(axis.text.x = element_text(angle = 45, hjust = 1))
product <- sentiment_df[sentiment_df$name == unique(sentiment_df$name)[1],]
tidy_table <- product %>% group_by(month = floor_date(review_date, unit = "month")) %>% summarise(ave_review_text_scores = sum(text_scores)/n(), ave_rating = sum(rating)/n())
tidy_table = data.frame(tidy_table)
p2 <- ggplot(tidy_table, aes(x = month))
p2 <- p2 + geom_line(aes(y = ave_review_text_scores, colour = "Sentiment Score"), size = 1)
# adding the relative ave_rating data, transformed to match roughly the range of the sentimental scores
p2 <- p2 + geom_line(aes(y = ave_rating/13, colour = "Ave_rating"), size = 1)
# now adding the secondary axis, following the example in the help file ?scale_y_continuous
# and, very important, reverting the above transformation
p2 <- p2 + scale_y_continuous(sec.axis = sec_axis(~.*13, name = "Average Rate stars"))
# modifying colours and theme options
p2 <- p2 + scale_colour_manual(values = c("steelblue2", "orange1"))
p2 <- p2 + labs(y = "Sentimental Scores",
x = "Month",
colour = "Parameter")
p2 <- p2 + theme(legend.position=c(0.8, 0.9))+ggtitle('Microsoft Surface Pro 4')
p2 <-p2 + theme_gray(base_size = 14) + theme(axis.text.x = element_text(angle = 45, hjust = 1))
grid.arrange(p1, p2, ncol=2)
Observation 1:
- We can see that for the product “”iHome Rechargeable Splash Proof Stereo Bluetooth Speaker“, the pattern of the average rate’s variation is similar to that of sentimental scores, which corresponds to our common sense. Therefore, in the future, we may fit a model by using
reviews.textto predict the missing data inreviews.rating.
Observation 2:
- However, for some products, such as “Microsoft Surface Pro 4”, the trend of the average ratings is not so corresponding to that of sentimental scores. Even in some months, they are in opposite directions. This problem can be explained by the graph in Interactive Component.
4.4: How are these above metioned patterns recognized among different brands?
ratings.brand = electron_data %>%
group_by(brand, reviews.rating) %>%
summarise(n = n()) %>%
transmute(reviews.rating, freq = n / sum(n))
ratings.brand$reviews.rating = factor(ratings.brand$reviews.rating)
ratings.brand$freq.5 = 2
for( i in 1:nrow(ratings.brand)){
bbrand = ratings.brand[i,]$brand
l5 = ratings.brand %>% filter(brand==bbrand, reviews.rating==5)
ratings.brand[i,]$freq.5 = l5$freq
}
#cbPalette <- c("peachpuff1", "orange1", "chocolate3", "steelblue3", "slategray2")
ggplot(ratings.brand, aes(x = reorder(brand, freq.5), y = freq, fill = reviews.rating)) +
geom_bar(stat = "identity", position = position_fill(reverse = TRUE)) +
xlab('brand') + ylab('percentage') +
ggtitle('How customers like the products from these brands?\n
from the most 5-star-level liked to the least') +
coord_flip() + scale_fill_manual(values = c("#99ff99", "#00ffcc", '#33cccc', '#99ccff', '#9966ff'))
Observation:
Similar to the divergent bichart for each product, for each brand, as the percentage of 5-star ratings decreases, the ‘center’ of the bar shifts to the left.
For those brands that have really low percentage of 5-star ratings, the percentage of 1-star ratings are not relatively high compare to other brands. However, the percentage of 3-star ratings increase as that of 5-star ratings decrease.